#include <monitor.h>
#include <common.h>
#include <colors.h>
#include <Starflub/string.h>

u16int *video_memory = (u16int *)0xB8000;
int pointerxy[2]={0,0}, savedmem=-1;

u8int cursor_x = 0;
u8int cursor_y = 0;
int freezePoint = 0;
u16int dump_mem[25*80];
u8int dumpcy, dumpcx;

void draw_mouse_at(int x, int y) {
	//this saves a single "pixel" of vid mem to an int.
	//later, that "pixel" is restored
	//this creates the illusion that the pointer is on
	//a layer above vid mem, which is actually untrue.
	if(savedmem==-1)
		savedmem = video_memory[XY2POS(0,0)];
	
	video_memory[XY2POS(pointerxy[0],pointerxy[1])]=savedmem;
	
	pointerxy[0] = x;
	pointerxy[1] = y;
	
	u8int attributeByte = (RED_COLOR<<4)|(WHITE_COLOR&0x0F);
	u16int blank = 0x20|(attributeByte<<8);
	
	savedmem = video_memory[XY2POS(x,y)];
	video_memory[XY2POS(x,y)] = blank;
}

static void move_cursor() {
	u16int cursorLocation = cursor_y*80+cursor_x;
	outb(0x3D4, 14);
	outb(0x3D5, cursorLocation>>8);
	outb(0x3D4, 15);
	outb(0x3D5, cursorLocation);
}

static void scroll() {
	u8int attributeByte = (BLACK_COLOR<<4)|(WHITE_COLOR&0x0F);
	u16int blank = 0x20|(attributeByte<<8);
	
	if(cursor_y >= 25) {
		int i;
		for(i = 0; i < 24*80; i++)
			video_memory[i] = video_memory[i+80];
		for(i = 24*80; i < 25*80; i++)
			video_memory[i] = blank;
		cursor_y=24;
	}
}

void monitor_draw_rect(int x, int y, int w, int h, enum MonitorColor col) {
	int i, j;
	u8int attributeByte = (col<<4)|(WHITE_COLOR&0x0F);
	u16int blank = 0x20|(attributeByte<<8);
	
	for(i = x; i < w; i++)
		for(j = y; j < h; j++)
			video_memory[XY2POS(i,j)]=blank;
}

void monitor_write_centered(char *str)
{
	int x = (80/2)-(strlen(str)/2);
	cursor_x=x;
//	int i;
//	for(i = 0; i < x; i++)
//		monitor_put(' ');
	monitor_write(str);
	monitor_write("\r\n");
	move_cursor();
	scroll();
}

void monitor_write_centered_bgcolored(char *str, enum MonitorColor bgcolor)
{
	int x = (80/2)-(strlen(str)/2);
	cursor_x=x;
//	int i;
//	for(i = 0; i < x; i++)
//		monitor_put(' ');
	monitor_write_bgcolored(str, bgcolor);
//	monitor_write_bgcolored("\r\n", bgcolor);
	move_cursor();
	scroll();
}

void monitor_dump_mem() {
	int i;
	for(i = 0; i < 25*80; i++)
		dump_mem[i] = video_memory[i];
	dumpcy = cursor_y;
	dumpcx = cursor_x;
}

void monitor_restore_mem() {
	int i;
	for(i=0; i < 25*80; i++)
		video_memory[i] = dump_mem[i];
	cursor_y = dumpcy;
	cursor_x = dumpcx;
	move_cursor();
}

void monitor_put(char c) {
	u8int backColor = BLACK_COLOR;
	u8int foreColor = WHITE_COLOR;

	u8int attributeByte = (backColor<<4)|(foreColor&0x0F);
	u16int attribute = attributeByte<<8;
	u16int *location;
	
	if(c == 0x08 && cursor_x)
		cursor_x--;
	else if(c == 0x09)
		cursor_x = (cursor_x+8)&~(8-1);
	else if(c == '\r')
		cursor_x = 0;
	else if(c == '\n') {
		cursor_x = 0;
		cursor_y++;
	}
	else if(c >= ' ') {
		location = video_memory + (cursor_y*80 + cursor_x);
		*location = c|attribute;
		cursor_x++;
	}
	
	if(cursor_x >= 80) {
		cursor_x = 0;
		cursor_y++;
	}
	
	scroll();
	move_cursor();
}

void monitor_put_colored(char c, enum MonitorColor color) {
	u8int backColor = BLACK_COLOR;
	u8int foreColor = color;

	u8int attributeByte = (backColor<<4)|(foreColor&0x0F);
	u16int attribute = attributeByte<<8;
	u16int *location;
	
	if(c == 0x08 && cursor_x)
		cursor_x--;
	else if(c == 0x09)
		cursor_x = (cursor_x+8)&~(8-1);
	else if(c == '\r')
		cursor_x = 0;
	else if(c == '\n') {
		cursor_x = 0;
		cursor_y++;
	}
	else if(c >= ' ') {
		location = video_memory + (cursor_y*80 + cursor_x);
		*location = c|attribute;
		cursor_x++;
	}
	
	if(cursor_x >= 80) {
		cursor_x = 0;
		cursor_y++;
	}
	
	scroll();
	move_cursor();
}

void monitor_put_bgcolored(char c, enum MonitorColor color) {
	u8int backColor = color;
	u8int foreColor = WHITE_COLOR;

	u8int attributeByte = (backColor<<4)|(foreColor&0x0F);
	u16int attribute = attributeByte<<8;
	u16int *location;
	
	if(c == 0x08 && cursor_x)
		cursor_x--;
	else if(c == 0x09)
		cursor_x = (cursor_x+8)&~(8-1);
	else if(c == '\r')
		cursor_x = 0;
	else if(c == '\n') {
		cursor_x = 0;
		cursor_y++;
	}
	else if(c >= ' ') {
		location = video_memory + (cursor_y*80 + cursor_x);
		*location = c|attribute;
		cursor_x++;
	}
	
	if(cursor_x >= 80) {
		cursor_x = 0;
		cursor_y++;
	}
	
	scroll();
	move_cursor();
}

void monitor_clear() {
	u8int attributeByte = (BLACK_COLOR<<4)|(WHITE_COLOR&0x0F);
	u16int blank = 0x20|(attributeByte<<8);	
	
	int i;
	for(i = 0; i < 80*25; i++)
		video_memory[i]=blank;
	
	cursor_x = 0;
	cursor_y = 0;
	move_cursor();
}

void monitor_clear_colored(enum MonitorColor col) {
	u8int attributeByte = (col<<4)|(WHITE_COLOR&0x0f);
	u16int blank = 0x20|(attributeByte<<8);
	
	int i;
	long j;
	for(i = 0; i < 80*25; i++)
		video_memory[i]=blank;
	
	cursor_x = 0;
	cursor_y = 0;
	move_cursor();
}

void monitor_write_hex(u32int n)
{
    s32int tmp;
    monitor_write("0x");
    char noZeroes = 1;
    int i;
    for (i = 28; i > 0; i -= 4)
    {
        tmp = (n >> i) & 0xF;
        if (tmp == 0 && noZeroes != 0)
            continue;
    
        if (tmp >= 0xA)
        {
            noZeroes = 0;
            monitor_put(SFCharToUpperCase(tmp-0xA+'a'));
        }
        else
        {
            noZeroes = 0;
            monitor_put(SFCharToUpperCase(tmp+'0'));
        }
    }
  
    tmp = n & 0xF;
    if (tmp >= 0xA)
        monitor_put(SFCharToUpperCase(tmp-0xA+'a'));
    else
        monitor_put(SFCharToUpperCase(tmp+'0'));
}


void move_cursor_to(int pos) {
	cursor_y = pos/80;
	cursor_x = pos-(cursor_y*80);
}

// this sets the index of video_memory at which you cannot backspace anymore.
// name suggestions are welcome :)
void monitor_set_freeze_point() {
	freezePoint = (cursor_y*80)+cursor_x-1;
}

void monitor_backspace() {
	if((cursor_y*80)+cursor_x-1 <= freezePoint)
		return;
	
	u8int attributeByte = (BLACK_COLOR<<4)|(WHITE_COLOR&0x0f);
	u16int blank = 0x20|(attributeByte<<8);
	attributeByte = (LRED_COLOR<<4)|(WHITE_COLOR&0x0f);
	u16int redblank = 0x20|(attributeByte<<8);
	int point=(cursor_y*80)+cursor_x-1;
	video_memory[point]=blank;
	
	int i;
	
	//this works, dunno why
	if(cursor_x == 0 && cursor_y > 0) {
		for(i = 80*(cursor_y)-1; i >= 80*cursor_y-80; i--) {
			if(video_memory[i]!=blank)
				break;
		}
		move_cursor_to(i+1);
	}
	else cursor_x--;
	
	scroll();
	move_cursor();
}

void monitor_write(char *c) {
	int i=0;
	while(c[i])
		monitor_put(c[i++]);
}

void monitor_write_colored(char *c, enum MonitorColor color) {
	int i=0;
	while(c[i])
		monitor_put_colored(c[i++], color);
}

void monitor_write_bgcolored(char *c, enum MonitorColor color) {
	int i=0;
	while(c[i])
		monitor_put_bgcolored(c[i++], color);
}

void monitor_write_dec (int dec)
{
	monitor_write(SFCStringFromInt(dec));
}

